/*
   Copyright (c) [2023] [魔凤啸天]
   [MFUI] is licensed under Mulan PSL v2.
   You can use this software according to the terms and conditions of the Mulan PSL v2. 
   You may obtain a copy of Mulan PSL v2 at:
            http://license.coscl.org.cn/MulanPSL2 
   THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.  
   See the Mulan PSL v2 for more details.  
*/
#pragma once
#include<SDL2/SDL.h>
#include<vector>
#include<unordered_map>

namespace MFUI{
	extern SDL_Cursor *cursor_ibem;
	bool openlib();
	void closelib();
	class Window;
	//用于两点确定指向属性 x,y,w,angle为输出
	void getpointto(SDL_FPoint& start,SDL_FPoint& end,float* x,float* y,float* w,double* angle);
	void getcenterrect(SDL_FRect* box,SDL_FRect* target,SDL_FRect* out);
	void getcenterx(SDL_FRect* box,SDL_FRect* target,float* x);
	void getcentery(SDL_FRect* box,SDL_FRect* target,float* y);
	void surfacedrawfpoint(SDL_Surface* s,SDL_FPoint p,SDL_Color c);
	void surfacedrawpoint(SDL_Surface* s,SDL_Point p,SDL_Color c);
	void surfacedrawfrect(SDL_Surface* s,SDL_FRect& r,SDL_Color c);
	void surfacefillfrect(SDL_Surface* s,SDL_FRect& r,SDL_Color c);
	void surfacedrawrect(SDL_Surface* s,SDL_Rect& r,SDL_Color c);
	void surfacefillrect(SDL_Surface* s,SDL_Rect& r,SDL_Color c);
	void surfacefblit(SDL_Surface* _src,SDL_FRect& src,SDL_Surface* _dst,SDL_FRect& dst);
	void surfaceblit(SDL_Surface* _src,SDL_Rect& src,SDL_Surface* _dst,SDL_Rect& dst);
	enum Align{
		align_nw=0,align_n,align_ne,
		align_w,align_m,align_e,
		align_sw,align_s,align_se
	};
	void getalignrect(SDL_FRect* box,SDL_FRect* target,Align align,SDL_FRect* out);
	class RefCounted{
		unsigned int _ref;
	protected:
		RefCounted();
		virtual ~RefCounted();

	public:
		void deref();
		void ref();
		unsigned int refcount()const;
	};
	class Window;
	class Renderer{
		friend class Window;
		SDL_Renderer* renderer;
		SDL_PixelFormat *format;
		Uint8 formatbytes;
		SDL_Texture* screen;
		Renderer(Window* w);
		~Renderer();
		void resize(int& w,int& h);

		public:
		//更新屏幕
		void present();
		//nullptr时为屏幕
		void settarget(SDL_Texture* t);
		SDL_Texture* gettarget();
		void setdefaulttarget();
		SDL_Texture* getdefaulttarget();
		void setdrawcolor(SDL_Color c);
		void drawpoint(int x,int y);
		void drawpointF(float x,float y);
		void drawline(int x1,int y1,int x2,int y2);
		void drawlineF(float x1,float y1,float x2,float y2);
		void drawrect(const SDL_Rect* r);
		void drawrectF(const SDL_FRect* r);
		void copy(SDL_Texture* src,const SDL_Rect* srcrect,const SDL_Rect* dstrect);
		void copyF(SDL_Texture* src,const SDL_Rect* srcrect,const SDL_FRect* dstrect);
		void copyEx(SDL_Texture* src,const SDL_Rect* srcrect,const SDL_Rect* dstrect,double angle,const SDL_Point* center,SDL_RendererFlip flip);
		void copyExF(SDL_Texture* src,const SDL_Rect* srcrect,const SDL_FRect* dstrect,double angle,const SDL_FPoint* center,SDL_RendererFlip flip);
		void fillrect(const SDL_Rect* r);
		void fillrectF(const SDL_FRect* r);
		void clear();
		SDL_Texture* texturefromsurface(SDL_Surface* s,SDL_BlendMode bm);
		//默认返回alpha混合的纹理
		SDL_Texture* texturefromfile(const char* path,SDL_BlendMode bm);
		//默认返回alpha混合的纹理
		SDL_Texture* texturefrommem(void* data,size_t size,SDL_BlendMode bm);
		SDL_Texture* createtexture(int w,int h,SDL_BlendMode bm,SDL_TextureAccess acc=SDL_TEXTUREACCESS_TARGET);
		Uint32 getformat()const;
		//基于渲染像素格式分配一维缓冲区 不用时需要free
		void* createimagebuffer(int w,int h);
		//返回当前创建缓冲区一个像素占用的字节数
		Uint8 getbufferbytes()const;
		//src不能为nullptr
		void gettexturepixel(SDL_Texture* texture,void* buffer,const SDL_Rect* src=nullptr);
		void gettexturepixel(SDL_Texture* texture,void* buffer,int w);
		void gettexturepixel(SDL_Texture* texture,void* buffer);
	};
	class Node:public RefCounted{
		friend class Window;
		bool focus;
	protected:
		virtual ~Node()override;
		virtual void _onfocus(Window* w);
		virtual void _losefocus(Window* w);

		std::vector<Node*> children;
		Node* parent;
		SDL_FRect rect;
		bool redraw,notified;//只读 需要重新计算布局 否则可以跳过绘制（不跳过遍历）
	public:
		Node();
		Node(std::initializer_list<Node*>& children);
		//遍历时筛除范围外的节点 容器节点仅刷新发生变化的子节点(除非布局参数发生变化)
		virtual void draw(Renderer* r,bool updatecache);
		//由根节点决定这一轮是否刷新绘制
		virtual bool confirmdraw();
		virtual void onadd(Window* w);
		virtual void onremove(Window* w);

		const SDL_FRect& getrect()const;
		void setrect(SDL_FRect& r);
		void setposition(SDL_FPoint p);
		void offsetposition(SDL_FPoint p);
		//如果需要刷新UI则需要配合notifyparent使用
		void forceredraw();
		void setsize(SDL_FPoint p);
		//谨慎使用，使用不当会导致各种问题
		void setparent(Node* p);
		void sortchildren(bool(*cmp)(Node*,Node*));
		void notifyparent();
		bool focused();
		//不存在返回nullptr
		Node* getchild(size_t index)const;
		Node* getparent()const;
		//需要自行判断边界
		virtual void removechild(Window* w,size_t index);
		//需要自行判断边界
		virtual void removechildren(Window* w,size_t start,size_t end);
		//需要自行判断是否还有子节点
		virtual void popchildren(Window* w,size_t n);
		//请勿重复添加
		virtual void addchild(Window* w,Node* n);
		//请勿重复添加
		virtual void addchildren(Window* w,std::initializer_list<Node*> l);
		//请勿重复添加
		virtual void insertchild(Window* w,Node* n,size_t index);
		//请勿重复添加
		virtual void insertchildren(Window* w,std::initializer_list<Node*> l,size_t index);
		//需要自行判断边界
		virtual void swapchild(size_t a,size_t b);
		size_t childcount();
	};
	//不覆盖背景的都需要缓存背景
	class CacheNode:public Node{
	protected:
		~CacheNode()override;
		SDL_Texture* cache;
		CacheNode();
		CacheNode(std::initializer_list<Node*>& children);
		void _draw(Renderer* r,bool updatecache);
		void _draw(Renderer* r,SDL_Rect& src,SDL_FRect& dst);
		void _draw(Renderer* r,SDL_FRect& src,SDL_FRect& dst);
	};
	class Window{
		friend class Renderer;
		static std::unordered_map<Uint32,Window*> windows;
		static void onwindowresize(Window*,Node*,SDL_WindowEvent*);
		void handleevent(SDL_Event& event);
	public:
		typedef void(*EventCallback)(Window*,Node*,SDL_Event*);
		typedef void(*WindowCallback)(Window*,Node*,SDL_WindowEvent*);
	private:
		int w,h;
		SDL_Window* window;
		Renderer renderer;
		Node* root;
		std::unordered_map<Uint32,std::unordered_map<Node*,EventCallback>> signals;
		std::unordered_map<Uint8,std::unordered_map<Node*,WindowCallback>> events;
		std::vector<Node*> tabs;
		std::unordered_map<Node*,unsigned int> tabcount;
	public:
		const Uint32 id;
		bool updatecache;
		//root随window销毁
		Window(const char* title,int width,int height,int flag,Node* root);
		virtual ~Window();
		//UI绘制和事件单线程处理 先事件后UI绘制
		static void main(Uint32 maxfps);
		//windowevent单独管理
		void connect(SDL_EventType type,Node* node,EventCallback callback);
		void disconnect(SDL_EventType type,Node* node);
		void connectwindow(SDL_WindowEventID type,Node* node,WindowCallback callback);
		void disconnectwindow(SDL_WindowEventID type,Node* node);
		void connectwindowresize(Node* node);
		void disconnectwindowresize(Node* node);
		//默认返回alpha混合的纹理
		SDL_Texture* texturefromsurface(SDL_Surface* s,SDL_BlendMode bm);
		//默认返回alpha混合的纹理
		SDL_Texture* texturefromfile(const char* path,SDL_BlendMode bm);
		//默认返回alpha混合的纹理
		SDL_Texture* texturefrommem(void* data,size_t size,SDL_BlendMode bm);
		SDL_Texture* createtexture(int w,int h,SDL_BlendMode bm,SDL_TextureAccess acc=SDL_TEXTUREACCESS_TARGET);
		SDL_Point getsize()const;
		SDL_FPoint getfsize()const;
		void setfocus(Node* n);
		void unsetfocus(Node* n);
		//UI线程原root->deref() 新root->ref()
		void setroot(Node* n);
		//缓存后前往指定页面
		void to(Node* n);
		//默认已缓存
		void back();
		//基于渲染像素格式分配一维缓冲区 不用时需要free
		void* createimagebuffer(int w,int h);
		//返回当前创建缓冲区一个像素占用的字节数
		Uint8 getbufferbytes()const;
		//src==nullptr时为整个，此时需要确保buffer大小符合texture大小
		void gettexturepixel(SDL_Texture* texture,void* buffer,const SDL_Rect* src=nullptr);
		virtual void update();
		//退出(closelib)调用
		static void closeall();
	};
}